import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UploadFile } from 'ng-zorro-antd';
import { Observable, of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

import { HttpServer } from '@core/net/http-server';
import { I18NService } from '@core/i18n/i18n.service';
import { Constant } from '@core/constant';
import { UrlManager } from '../url/url.manager';
import { callType, messageFormat } from '../utils/data.utils';

interface Info {
  file: UploadFile;
  fileList: Array<any>;
  event: any;
}

const imageFileType = 'image/png,image/jpeg,image/gif,image/bmp';

@Component({
  selector: 'app-oss-upload-file',
  template: `
    <nz-upload
      [nzDisabled]="disabled || loading"
      [nzBeforeUpload]="beforeUpload"
      [nzHeaders]="_config.nzHeaders"
      [nzListType]="_config.nzListType"
      [nzMultiple]="_config.nzMultiple"
      [nzLimit]="_config.nzLimit"
      [nzWithCredentials]="_config.nzWithCredentials"
      [(nzFileList)]="fileList"
      (nzChange)="handleChange($event)"
    >
      <button nz-button type="button" [nzLoading]="loading" [disabled]="disabled">
        <i class="anticon anticon-upload" *ngIf="!loading"></i><span>{{ 'modify.text.upload' | translate }}</span>
      </button>
    </nz-upload>
    <nz-alert *ngIf="error" nzType="error" [nzMessage]="error" nzShowIcon></nz-alert>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => OssUploadFilesComponent),
    multi: true
  }]
})

export class OssUploadFilesComponent implements ControlValueAccessor {

  constructor(
    private http: HttpServer,
    private i18nService: I18NService
  ) { }

  private propagateChange: any = {};

  private _fileList: Array<any> = [];

  _config: any = {
    // 限制文件类型
    nzFileType: 'image/png,image/jpeg,image/jpg,image/gif,image/bmp',
    // 请求头
    nzHeaders: (file: UploadFile) => {
      return {};
    },
    // 上传列表的内建样式，支持三种基本样式 text, picture 和 picture-card
    nzListType: 'picture',
    // 是否允许上传多张和多选
    nzMultiple: false,
    // 多张限制
    nzLimit: 0,
    // 请求是否带cookie
    nzWithCredentials: false,
    // 文件大小限制 默认：10M
    fileSize: 10485760,
    // 是否开启随机名称
    randomName: true,
    reSize: false,
    width: 300,
    height: 300
  };

  error: string;

  loading = false;

  get fileList() {
    return this._fileList;
  }

  set fileList(fileList) {
    if (fileList !== null) {
      this._fileList = fileList;
    }
  }

  /**
   * 获取外部配置
   */
  @Input('config')
  set config(config: object) {
    this._config = Object.assign({}, this._config, config);
  }

  /**
   * 是否禁用
   */
  get disabled(): boolean {
    let check = false;
    if (!this._config.nzMultiple && this.fileList.length >= 1) {
      check = true;
    }
    return check;
  }

  writeValue(val: any): void {
    if (val !== null) {
      if (callType.call(val, callType.type.string)) {
        this._fileList = [this.handleParam(val)];
      } else if (callType.call(val, callType.type.array)) {
        this._fileList = val.map(v => {
          return this.handleParam(v);
        });
      }
    }
  }

  registerOnChange(fn: any): void {
    this.propagateChange = fn;
  }

  registerOnTouched(fn: any): void { }

  beforeUpload = (file: UploadFile): boolean => {
    this.loading = true;
    if (this.checkFile(file)) {
      this.upload(file);
    } else {
      this.loading = false;
    }
    return false;
  }

  /**
   * 验证文件
   * @param  file 文件
   * @return
   */
  private checkFile(file: UploadFile): boolean {
    // 文件类型验证
    const typeRes: boolean = this._config.nzFileType.includes(file.type);
    if (!typeRes || file.type === '') {
      this.error = this.i18nService.translate('modify.return.file_type_err');
      return false;
    }
    // 文件大小验证
    const sizeRes = file.size > this._config.fileSize;
    if (sizeRes) {
      this.error = this.i18nService.translate('modify.return.file_size_err') + this._config.fileSize + 'b';
      return false;
    }
    this.error = null;
    return true;
  }

  /**
   * 上传
   * @param  file 上传的文件
   */
  private upload(file: UploadFile): void {
    this.createPolicy(file.name).subscribe(res => {
      const formData = new FormData();
      Object.keys(res).filter(v => v !== 'host').forEach((v) => {
        formData.append(v, res[v]);
      });
      formData.append('file', file as any);
      formData.append('success_action_status', '200');
      this.http.request('POST', res.host, formData)
        .pipe(
          mergeMap(data => {
            return of(data.status === 200);
          })
        )
        .subscribe(result => {
          if (result) {
            const data = {
              uid: file.uid,
              name: this.handleResize(res.key, file.type),
              thumbUrl: this.handleResize(UrlManager.filePrefix + res.key, file.type),
              status: 'ok'
            };
            this._fileList.push(data);
            this._fileList = this._fileList.filter(w => w.status === 'ok');
            this.handleFileList(this.fileList.map(w => w.name));
          }
          this.loading = false;
        }, err => {
          this.loading = false;
        });
    }, err => {
      this.loading = false;
    });
  }

  /**
   * 获取上传授权信息
   * @param  fileName 文件名
   * @return
   */
  private createPolicy(fileName: string): Observable<any> {
    return this.http.get(UrlManager.upload.createPolicy, {
      fileName: fileName,
      randomName: this._config.randomName
    });
  }

  /**
   * 文件变动监听
   * @param {Info} info [description]
   */
  handleChange(info: Info): void {
    const status = info.file.status;
    if (status === 'removed') {
      this.handleRemove(info.file);
    }
  }

  /**
   * 封装返回数据并回传
   * @param {Array<any>} fileList [description]
   */
  private handleFileList(fileList: Array<any>): void {
    this.propagateChange(fileList);
  }

  private handleRemove(file: UploadFile): void {
    this.fileList = this.fileList.filter(w => w.uid !== file.uid);
    this.handleFileList(this.fileList.map(w => w.name));
  }

  /**
   * 封装列表参数
   * @param  {any} val [description]
   * @return {any}     [description]
   */
  private handleParam(val: any): any {
    return {
      name: val,
      response: {
        dataSet: val
      },
      url: val,
      thumbUrl: UrlManager.filePrefix + val
    };
  }

  private handleResize(path: string, type: string): string {
    if (imageFileType.includes(type) && this._config.reSize) {
      return messageFormat(Constant.OSS_IMAGE_RESIZE, path, this._config.height, this._config.width);
    }
    return path;
  }
}
